#include <stdio.h>
#include <string.h>
#include "crc16.h"
#include "event_notifier.h"


static struct notifier_manager en_manager = {0};

/*! \enum event_type_value
 *
 *  Detailed description
 */
enum event_type_value {
    EVENT_NOT_SET = -1,
};

/*! \enum event_priority_value
 *
 *  Detailed description
 */
enum event_priority_value {
    DEFAULT_PRIORYITY,
};

#define EVENT_NODE_ID_VALE_NOT_SET -1

static struct notifier_manager *_get_en_manager_object(void)
{
    return &en_manager;
}

API int event_notifier_get_cur_event_cnt(void)
{
    struct notifier_manager *pen_manager = _get_en_manager_object();
    return pen_manager->event_notifier_cur_size;
}

API int event_notifier_get_size(void)
{
    struct notifier_manager *pen_manager = _get_en_manager_object();
    return pen_manager->event_notifier_size;
}

static inline void _event_node_reset(struct notifier_node *node)
{
    if(!node) return;
    //printf("Reset %p, with id %x\n", node, node->node_id);
    node->node_id    = EVENT_NODE_ID_VALE_NOT_SET;
    node->event_type = EVENT_NOT_SET;
    node->event_callback_arg = NULL;
    node->event_callback = NULL;
    node->priority   = DEFAULT_PRIORYITY;
    //node->next       = NULL;
}

static inline int __event_node_compare(struct notifier_node *a,
                                       struct notifier_node *b)
{
    return a->priority > b->priority;
}

static inline void __event_node_cp(struct notifier_node *src,
                                  struct notifier_node *dest)
{
    //printf("Cp %p to %p\n", src, dest);
    dest->node_id     = src->node_id;
    dest->event_type  = src->event_type;
    dest->event_callback_arg = src->event_callback_arg;
    dest->event_callback     = src->event_callback;
    dest-> priority  = src->priority;
}

static inline void __event_node_swap(struct notifier_node *a,
                                     struct notifier_node *b)
{
    struct notifier_node tmp;
    printf("swap %p %p\n",a,b);
    // save
    tmp.node_id            = a->node_id;
    tmp.event_type         = a->event_type;
    tmp.event_callback_arg = a->event_callback_arg;
    tmp.event_callback     = a->event_callback;
    tmp.priority           = a->priority;
    // write a
    a->node_id            = b->node_id;
    a->event_type         = b->event_type;
    a->event_callback_arg = b->event_callback_arg;
    a->event_callback     = b->event_callback;
    a->priority           = b->priority;
    // restore a to b
    b->node_id            = tmp.node_id;
    b->event_type         = tmp.event_type;
    b->event_callback_arg = tmp.event_callback_arg;
    b->event_callback     = tmp.event_callback;
    b->priority           = tmp.priority;
}

/* 
 * 事件节点排序
 * 描述：
 *     确保注册的事件数组中没有空缺
 *     常用于 添加/撤销事件登记通知 后的清理工作
 *
 */
static void _event_node_array_sort(struct notifier_manager *en_manager)
{
    int i, j;
	int isSorted;
    struct notifier_node *notifier_node;
    struct notifier_node *notifier_nodej;

    //printf("Sort\n");
	// 去掉空白的时间节点（通过数组前移的方式）
    for (i = 0, notifier_node = en_manager->notifiers_head;
         i < event_notifier_get_size();
         i++, notifier_node++)
    {
        if(notifier_node->event_type != EVENT_NOT_SET)
        {
            continue;
        }

        for (j = i, notifier_nodej = notifier_node;
             j < event_notifier_get_size() -1;
             j++, notifier_nodej++)
        {
            if((notifier_node+1)->event_type != EVENT_NOT_SET)
            {
#if 1
                __event_node_swap(notifier_node, notifier_node+1);
#else
                __event_node_cp(notifier_nodej + 1, notifier_nodej);
                _event_node_reset(notifier_nodej + 1);
#endif
            }
        }
    }

    // 根据 priority 排序（冒泡排序的优化）
    for (i = 0, notifier_node = en_manager->notifiers_head;
         i < event_notifier_get_cur_event_cnt() - 1;
         i++, notifier_node++)
    {
        isSorted = 1;  //假设剩下的元素已经排序好了
        for(j = 0; j < event_notifier_get_cur_event_cnt() - 1 - i; j++)
        {
            if(__event_node_compare(notifier_node, notifier_node+1))
            {
                __event_node_swap(notifier_node, notifier_node+1);
                isSorted = 0;  //一旦需要交换数组元素，就说明剩下的元素没有排序好
            }
        }
        if(isSorted) break; //如果没有发生交换，说明剩下的元素已经排序好了
    }
}

API int event_notifier_init(struct notifier_node *header, int size)
{
    int i;
    struct notifier_node *notifier_node;
    struct notifier_manager *pen_manager = _get_en_manager_object();

    if(!header) return EN_RET_ERR_ARG;

    pen_manager->event_notifier_size = size;
    pen_manager->event_notifier_cur_size = 0;
    pen_manager->next = NULL;

    pen_manager->notifiers_head     = header;
    memset(pen_manager->notifiers_head, 0, sizeof(struct notifier_node)*size);

    for (i = 0, notifier_node = pen_manager->notifiers_head;
         i < event_notifier_get_size();
         i++, notifier_node++)
    {
        _event_node_reset(notifier_node);
    }

    return EN_RET_OK;
}

API int event_notifier_init_get_cur_size(void)
{
    return _get_en_manager_object()->event_notifier_cur_size;
}

/*! \enum notifier_event_find_flag
 *
 *  Detailed description
 */
enum notifier_event_find_flag {
    NOTIFIER_EVENT_FIND_FLAG_SAME_EVENT     = 1 << 0,
    NOTIFIER_EVENT_FIND_FLAG_SM_EVT_SM_HASH = 1 << 1,
};

static unsigned int __event_notifier_generate_id(char *buf, int len)
{
    const char*const_buf = (char*)buf;
    unsigned int crc = 0x0;
    if(!buf) return EN_RET_ERR_ARG;

    return crc16(crc, const_buf, len);
}

//int 生根据event_type, arg, cbfun 成ID
static unsigned int _event_notifier_generate_id_from_node(struct notifier_node* node)
{
    unsigned int buff[3];
    char * cbuf = (char*)buff;

    if(!node) return EN_RET_ERR_ARG;

    buff[0] = (unsigned int)node->event_type;
    buff[1] = (unsigned int)node->event_callback_arg;
    buff[2] = (unsigned int)node->event_callback;

    return __event_notifier_generate_id(cbuf, sizeof(buff));
}

API int event_notify(int event_type)
{
    int i;
    struct notifier_manager *pen_manager = _get_en_manager_object();
    struct notifier_node *notifier_node;
    int notiiy_cnt = 0;
    if(event_type == EVENT_NOT_SET) return EN_RET_OK;

    for (i = 0, notifier_node = pen_manager->notifiers_head;
         i < event_notifier_get_size();
         i++, notifier_node++) {

        if(notifier_node->event_type == event_type)
        {
            notifier_node->event_callback(notifier_node->event_callback_arg);
            notiiy_cnt++;
        }
    }

    return notiiy_cnt;
}
API int event_notifier_register_event(int event_type,
                                  void* (*event_callback)(void *event_callback_arg),
                                  void *event_callback_arg)
{
    return event_notifier_register_event_with_priority(event_type,  DEFAULT_PRIORYITY,
                                                    event_callback, event_callback_arg);
}

API int event_notifier_register_event_with_priority(int event_type,
                                                    int priority,
                                                    void* (*event_callback)(void *event_callback_arg),
                                                    void *event_callback_arg)
{
    struct notifier_manager *pen_manager = _get_en_manager_object();
    int i;
    int flag = 0;
    int id_cur;
    int id_arg;
    struct notifier_node *notifier_node = pen_manager->notifiers_head;
    struct notifier_node  tmp_node;

    if(event_type == EVENT_NOT_SET) return EN_RET_ERR_ARG;
    if(event_callback == NULL)      return EN_RET_ERR_ARG;
    if(pen_manager->event_notifier_cur_size >= event_notifier_get_size())
        return EN_RET_FULL;


    tmp_node.event_type         = event_type;
    tmp_node.event_callback_arg = event_callback_arg;
    tmp_node.event_callback     = event_callback;
    id_arg = _event_notifier_generate_id_from_node(&tmp_node);


    for (i = 0, notifier_node = pen_manager->notifiers_head;
         i < event_notifier_get_size();
         i++, notifier_node++) {

        //printf("%d\n", notifier_node->event_type);
        id_cur = notifier_node->node_id;
        //printf("CHECK ID : cur %4x, arg %4x\n", id_cur,  id_arg);

        if(id_cur == id_arg)
        {
            flag |= NOTIFIER_EVENT_FIND_FLAG_SAME_EVENT;
            break;
        }
    }

    if(flag &= NOTIFIER_EVENT_FIND_FLAG_SAME_EVENT)
    {
        if(priority != DEFAULT_PRIORYITY)
        {
            notifier_node->priority = priority;
            _event_node_array_sort(pen_manager);
            return EN_RET_OK;
        }
        return EN_RET_ERR_ARG;
    }

    /* Insert */

    for (i = 0, notifier_node = pen_manager->notifiers_head;
         i < event_notifier_get_size();
         i++, notifier_node++)
    {

        //printf("%d\n", notifier_node->event_type);
        if(notifier_node->event_type == EVENT_NOT_SET)
        {
            notifier_node->event_type         = event_type;
            notifier_node->priority           = priority;
            notifier_node->event_callback_arg = event_callback_arg;
            notifier_node->event_callback     = event_callback;
            notifier_node->node_id = _event_notifier_generate_id_from_node(notifier_node);
            pen_manager->event_notifier_cur_size ++;

            break;
        }
    }
    printf("Insert Event Node %p, id is [%x]\n", notifier_node, id_arg);

    _event_node_array_sort(pen_manager);

    return EN_RET_OK;
}


API int event_notifier_unregister_event(int event_type,
                                  void* (*event_callback)(void *event_callback_arg),
                                  void *event_callback_arg)
{
    struct notifier_manager *pen_manager = _get_en_manager_object();
    int i;
    int flag = 0;
    int id_cur;
    int id_arg;
    struct notifier_node *notifier_node = pen_manager->notifiers_head;
    struct notifier_node  tmp_node;

    if(event_type == EVENT_NOT_SET) return EN_RET_ERR_ARG;
    if(event_callback == NULL)      return EN_RET_ERR_ARG;

    tmp_node.event_type         = event_type;
    tmp_node.event_callback_arg = event_callback_arg;
    tmp_node.event_callback     = event_callback;
    id_arg = _event_notifier_generate_id_from_node(&tmp_node);

    for (i = 0, notifier_node = pen_manager->notifiers_head;
         i < event_notifier_get_size();
         i++, notifier_node++) {

        id_cur = notifier_node->node_id;
        //printf("CHECK ID : cur %4x, arg %4x\n", id_cur,  id_arg);

        if(id_cur == id_arg)
        {
            flag |= NOTIFIER_EVENT_FIND_FLAG_SAME_EVENT;
            break;
        }
    }

    // 如果事件相同，则进一步对比 函数与参数是否相同
    if (flag &= NOTIFIER_EVENT_FIND_FLAG_SAME_EVENT)
    {
        goto unregister;
    }

    return EN_RET_OK;

unregister:
    printf("Remove node %p\n", notifier_node);
    _event_node_reset(notifier_node);
    _event_node_array_sort(pen_manager);
    pen_manager->event_notifier_cur_size--;

    return EN_RET_OK;
}

